/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    control.c

Abstract:

    User-mode -> Kernel-mode PnP Manager control routines.

Author:

    Lonny McMichael (lonnym) 02/14/95

Revision History:

--*/

#include "pnpmgrp.h"
#pragma hdrstop


//
// Global driver object that is used by calls to NtPlugPlayControl
// with control type of PlugPlayControlDetectResourceConflict.
//
PDRIVER_OBJECT driverObject = NULL;


#ifdef _PNP_POWER_

//
// Define the context structure for the PiDevicePathToServiceInstance
// callback routine.
//
typedef struct _PI_DEVPATH_TO_SVCINST_CONTEXT {
    NTSTATUS ReturnStatus;
    PUNICODE_STRING DevicePath;
    UNICODE_STRING DeviceInstanceMatch;
} PI_DEVPATH_TO_SVCINST_CONTEXT, *PPI_DEVPATH_TO_SVCINST_CONTEXT;

//
// Prototype utility functions internal to this file.
//
BOOLEAN
PiDevicePathToServiceInstance(
    IN     HANDLE DeviceInstanceHandle,
    IN     PUNICODE_STRING DeviceInstancePath,
    IN OUT PVOID Context
    );

#endif // _PNP_POWER_

NTSTATUS
PiGenerateLegacyDeviceInstance(
    IN  PUNICODE_STRING ServiceKeyName,
    OUT PWSTR DeviceInstance,
    IN  ULONG DeviceInstanceLength,
    OUT PULONG RequiredLength
    );

NTSTATUS
PiDetectResourceConflict(
    IN PCM_RESOURCE_LIST  ResourceList,
    IN ULONG              ResourceListSize
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtPlugPlayControl)
#pragma alloc_text(PAGE, PiGenerateLegacyDeviceInstance)
#pragma alloc_text(PAGE, PiDetectResourceConflict)

#ifdef _PNP_POWER_
#pragma alloc_text(PAGE, PiQueryRemoveDevice)
#pragma alloc_text(PAGE, PiRemoveDevice)
#pragma alloc_text(PAGE, PiCancelRemoveDevice)
#pragma alloc_text(PAGE, PiAddDevice)
#pragma alloc_text(PAGE, PiEjectDevice)
#pragma alloc_text(PAGE, PiUnlockDevice)
#pragma alloc_text(PAGE, PiQueryDeviceCapabilities)
#pragma alloc_text(PAGE, PiGetDevicePathInformation)
#endif // _PNP_POWER_

#endif // ALLOC_PRAGMA

NTSTATUS
NtPlugPlayControl(
    IN     PLUGPLAY_CONTROL_CLASS PnPControlClass,
    IN OUT PVOID PnPControlData,
    IN     ULONG PnPControlDataLength,
    OUT    PULONG RequiredLength        OPTIONAL
    )

/*++

Routine Description:

    This Plug and Play Manager API provides a mechanism for the user-mode
    PnP Manager to control the activity of its kernel-mode counterpart.

Arguments:

    PnPControlClass - Specifies what action to perform.

    PnPControlData - Supplies a pointer to data specific to this action.

    PnPControlDataLength - Specifies the size, in bytes, of the buffer pointed
                           to by PnPControlData

    RequiredLength - Optional pointer to a variable the receives the actual
                     size required to store the output data in
                     PnPControlData.

Return Value:

    NT status code indicating success or failure.  Set of possible return
    values includes the following:

        STATUS_SUCCESS - normal, successful completion.

        STATUS_INVALID_PARAMETER_1 - The PnPControlClass parameter did not
            specify a valid control class.

        STATUS_INVALID_PARAMETER_MIX - The value of the PnPControlDataLength
            parameter did not match the length required for the control
            class requested by the PnPControlClass parameter.

        STATUS_BUFFER_TOO_SMALL - The size of the supplied output buffer is not
            large enough to hold the output generated by this control class.

        STATUS_ACCESS_VIOLATION - One of the following pointers specified
            an invalid address: (1) the PnPControlData buffer pointer,
            (2) some pointer contained in the PnPControlData buffer, or
            (3) the RequiredLength pointer

        STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
            for this request to complete.

--*/

{
    NTSTATUS Status;
    KPROCESSOR_MODE PreviousMode;
    PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData;
    PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA LegacyDevGenData;
    PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA DeviceResourceData;
#ifdef _PNP_POWER_
    PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA DeviceCapabilitiesData;
    PPLUGPLAY_CONTROL_DEVICE_PATH_DATA DevicePathData;
    ULONG ReturnBufferSize;
#endif

    try {
        //
        // Get previous processor mode and probe arguments if necessary.
        //
        PreviousMode = KeGetPreviousMode();
        if(PreviousMode != KernelMode) {

            ProbeForWrite(PnPControlData, PnPControlDataLength, sizeof(ULONG));

            if(ARGUMENT_PRESENT(RequiredLength)) {
                ProbeForWriteUlong(RequiredLength);
            }
        }

        switch(PnPControlClass) {

        case PlugPlayControlQueryRemoveDevice:
        case PlugPlayControlRemoveDevice:
        case PlugPlayControlCancelRemoveDevice:
        case PlugPlayControlAddDevice:
        case PlugPlayControlEjectDevice:
        case PlugPlayControlUnlockDevice:
        case PlugPlayControlEnumerateDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#endif

        case PlugPlayControlRegisterNewDevice:
        case PlugPlayControlDeregisterDevice:
            //
            // Validate buffer for all control classes using a
            // PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA structure.
            //
            if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) {
                return STATUS_INVALID_PARAMETER_MIX;
            }

            DeviceControlData = (PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)PnPControlData;

            if(PreviousMode != KernelMode) {
                ProbeForRead(DeviceControlData->DeviceInstance.Buffer,
                             DeviceControlData->DeviceInstance.Length,
                             sizeof(WCHAR)
                            );
            }

            //
            // Since this structure is a fixed size, store RequiredLength now, if necessary,
            // so we don't have to do it for each class separately.
            //
            if(ARGUMENT_PRESENT(RequiredLength)) {
                *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA);
            }

            break;

        case PlugPlayControlQueryDeviceCapabilities:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            //
            // Validate buffer for all control classes using a
            // PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA structure.
            //
            if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)) {
                return STATUS_INVALID_PARAMETER_MIX;
            }

            DeviceCapabilitiesData =
                        (PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)PnPControlData;

            if(PreviousMode != KernelMode) {
                ProbeForRead(DeviceCapabilitiesData->DeviceInstance.Buffer,
                             DeviceCapabilitiesData->DeviceInstance.Length,
                             sizeof(WCHAR)
                            );
            }
#endif
            break;

        case PlugPlayControlGetDevicePathInformation:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            //
            // Validate buffer for all control classes using a
            // PLUGPLAY_CONTROL_DEVICE_PATH_DATA structure.
            //
            if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_DEVICE_PATH_DATA)) {
                return STATUS_INVALID_PARAMETER_MIX;
            }

            DevicePathData = (PPLUGPLAY_CONTROL_DEVICE_PATH_DATA)PnPControlData;

            if(PreviousMode != KernelMode) {
                ProbeForRead(DevicePathData->DevicePath.Buffer,
                             DevicePathData->DevicePath.Length,
                             sizeof(WCHAR)
                            );
            }
#endif
            break;

        case PlugPlayControlGenerateLegacyDevice:
            //
            // Validate buffer for all control classes using a
            // PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA structure.
            //
            if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)) {
                return STATUS_INVALID_PARAMETER_MIX;
            }

            LegacyDevGenData = (PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)PnPControlData;

            if(PreviousMode != KernelMode) {
                ProbeForRead(LegacyDevGenData->ServiceName.Buffer,
                             LegacyDevGenData->ServiceName.Length,
                             sizeof(WCHAR)
                            );
            }
            break;

        case PlugPlayControlDetectResourceConflict:
            //
            // Determine whether resource list specified in buffer
            // is avaiable (not conflicting with other devices).
            //
            if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)) {
                return STATUS_INVALID_PARAMETER_MIX;
            }

            DeviceResourceData = (PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)PnPControlData;

            if(PreviousMode != KernelMode) {
                ProbeForRead(DeviceResourceData->DeviceInstance.Buffer,
                             DeviceResourceData->DeviceInstance.Length,
                             sizeof(WCHAR)
                             );

                ProbeForRead(DeviceResourceData->ResourceList,
                             DeviceResourceData->ResourceListSize,
                             sizeof(UCHAR)
                             );
            }

            //
            // Since this structure is a fixed size, store RequiredLength now, if necessary,
            // so we don't have to do it for each class separately.
            //
            if(ARGUMENT_PRESENT(RequiredLength)) {
                *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA);
            }
            break;

        default:
            //
            // Invalid control class.
            //
            return STATUS_INVALID_PARAMETER_1;
        }

        //
        // Now invoke the proper routine for this control class.
        //
        switch(PnPControlClass) {

        case PlugPlayControlQueryRemoveDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiQueryRemoveDevice(&(DeviceControlData->DeviceInstance),
                                         &(DeviceControlData->Status)
                                        );
#endif
            break;

        case PlugPlayControlRemoveDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiRemoveDevice(&(DeviceControlData->DeviceInstance),
                                    &(DeviceControlData->Status)
                                   );
#endif
            break;

        case PlugPlayControlCancelRemoveDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiCancelRemoveDevice(&(DeviceControlData->DeviceInstance),
                                          &(DeviceControlData->Status)
                                         );
#endif
            break;

        case PlugPlayControlAddDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiAddDevice(&(DeviceControlData->DeviceInstance),
                                 &(DeviceControlData->Status)
                                );
#endif
            break;

        case PlugPlayControlEjectDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiEjectDevice(&(DeviceControlData->DeviceInstance),
                                   &(DeviceControlData->Status)
                                  );
#endif
            break;

        case PlugPlayControlUnlockDevice:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiUnlockDevice(&(DeviceControlData->DeviceInstance),
                                    &(DeviceControlData->Status)
                                   );
#endif
            break;

        case PlugPlayControlQueryDeviceCapabilities:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            //
            // BUGBUG (lonnym): need to add argument for slot capabilities once it
            // is defined by KenR.
            //
            Status = PiQueryDeviceCapabilities(&(DeviceCapabilitiesData->DeviceInstance)
                                              );

            if(ARGUMENT_PRESENT(RequiredLength)) {
                *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA);
            }
#endif
            break;

        case PlugPlayControlGetDevicePathInformation:
#ifndef _PNP_POWER_
            return STATUS_NOT_IMPLEMENTED;
#else
            Status = PiGetDevicePathInformation(
                        &(DevicePathData->DevicePath),
                        DevicePathData->ServiceName,
                        PnPControlDataLength -
                            FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName),
                        &ReturnBufferSize,
                        &(DevicePathData->ServiceNameLength),
                        &(DevicePathData->DeviceInstanceOffset),
                        &(DevicePathData->DeviceInstanceLength),
                        &(DevicePathData->ServiceInstanceOrdinal)
                       );

            if(ARGUMENT_PRESENT(RequiredLength)) {

                if(NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {

                    *RequiredLength = ReturnBufferSize +
                        FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName);
                }
            }
#endif
            break;

        case PlugPlayControlRegisterNewDevice:
            Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, TRUE);
            DeviceControlData->Status = Status;
            break;

        case PlugPlayControlDeregisterDevice:
            Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, FALSE);
            DeviceControlData->Status = Status;
            break;

        case PlugPlayControlEnumerateDevice:
            //
            // BUGBUG (lonnym): This is where a call to PiEnumerateDevice() should go.
            // This API was previously the NT API, NtEnumerateBus.
            //
            Status = STATUS_NOT_IMPLEMENTED;
            break;

        case PlugPlayControlGenerateLegacyDevice:
            Status = PiGenerateLegacyDeviceInstance(
                         &LegacyDevGenData->ServiceName,
                         LegacyDevGenData->DeviceInstance,
                         PnPControlDataLength -
                            FIELD_OFFSET(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA, DeviceInstance),
                         &LegacyDevGenData->DeviceInstanceLength
                        );

            if(ARGUMENT_PRESENT(RequiredLength)) {
                if(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) {
                    *RequiredLength = LegacyDevGenData->DeviceInstanceLength +
                                          sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA);
                }
            }
            break;

        case PlugPlayControlDetectResourceConflict:
            Status = PiDetectResourceConflict(DeviceResourceData->ResourceList,
                                              DeviceResourceData->ResourceListSize);
            DeviceResourceData->Status = Status;
            break;

        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }

    return Status;
}

NTSTATUS
PiGenerateLegacyDeviceInstance(
    IN  PUNICODE_STRING ServiceKeyName,
    OUT PWSTR DeviceInstance,
    IN  ULONG DeviceInstanceLength,
    OUT PULONG RequiredLength
    )

/*++

Routine Description:

    This routine creates a new instance node under System\Enum\Root\LEGACY_<Name>
    key and all the required default value entries.  Also a value entry under
    Service\ServiceKeyName\Enum is created to point to the newly created madeup
    entry.  A handle and the keyname of the new key are returned to caller.
    Caller must free the unicode string when he is done with it.

Arguments:

    ServiceKeyName - Supplies a pointer to the name of the subkey in the
        system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
        that caused the driver to load.

    DeviceInstance - Supplies a pointer to the character buffer that receives the
        newly-generated device instance name.

    DeviceInstanceLength - Supplies the size, in bytes, of the DeviceInstance
        buffer.

    RequiredLength - Supplies a pointer to a variable that receives the size,
        in bytes (excluding terminating NULL) of the device instance name stored
        in the buffer.

Return Value:

    A NTSTATUS code.
    If the Lengacy Device Instance exists already, this function returns sucessful.

--*/

{
    NTSTATUS status;
    HANDLE handle;
    ULONG junk;
    BOOLEAN isPlugPlayDriver = FALSE;
    PKEY_VALUE_FULL_INFORMATION keyValueInformation;
    UNICODE_STRING tempUnicodeString;

    KeEnterCriticalRegion();
    ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);

    status = IopOpenServiceEnumKeys(ServiceKeyName,
                                    KEY_READ,
                                    &handle,
                                    NULL,
                                    TRUE
                                   );
    if (NT_SUCCESS(status)) {

        //
        // Check whether this madeup key should be created.  It must be a legacy driver
        // to create the madeup key.
        //

        status = IopGetRegistryValue(handle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation);
        ZwClose(handle);

        if (NT_SUCCESS(status)) {
            if ((keyValueInformation->Type == REG_DWORD) &&
                (keyValueInformation->DataLength >= sizeof(ULONG))) {
                isPlugPlayDriver = TRUE;
                status = STATUS_INVALID_PARAMETER_2;
            }
            ExFreePool(keyValueInformation);
        }
        if (!isPlugPlayDriver) {
            status = IopCreateMadeupNode(ServiceKeyName,
                                         &handle,
                                         &tempUnicodeString,
                                         &junk,
                                         TRUE
                                         );
            if (NT_SUCCESS(status)) {

                //
                // We have successfully retrieved the newly-generated device instance name.
                // Now store it in the supplied buffer.
                //

                ZwClose(handle);
                *RequiredLength = tempUnicodeString.Length;

                if(DeviceInstanceLength >= tempUnicodeString.Length + sizeof(UNICODE_NULL)) {
                    try {
                        RtlMoveMemory(DeviceInstance,
                                      tempUnicodeString.Buffer,
                                      tempUnicodeString.Length + sizeof(UNICODE_NULL)
                                     );
                    } except(EXCEPTION_EXECUTE_HANDLER) {
                        status = GetExceptionCode();
                    }
                } else {
                    status = STATUS_BUFFER_TOO_SMALL;
                }
                RtlFreeUnicodeString(&tempUnicodeString);
            }
        }
    }

    ExReleaseResource(&PpRegistryDeviceResource);
    KeLeaveCriticalRegion();

    return status;
}
#ifdef _PNP_POWER_

NTSTATUS
PiQueryRemoveDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine queries a device driver for whether a particular device
    instance can be removed.

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance being query-removed.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the driver in response to the
        query-remove.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the driver's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiRemoveDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine causes a device driver to remove a particular device
    instance from the system.

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance to be removed.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the driver in response to the
        remove request.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the driver's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiCancelRemoveDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine cancels a previously-submitted request to query-remove
    a device.  It should only be called if the device instance was
    previously successfully query-removed.

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance for which a remove
        request is to be cancelled.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the driver in response to the
        remove cancel request.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the driver's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiAddDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine causes a device driver to add a new device instance
    (i.e., create a new device object).

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the new device instance to be added.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the driver in response to the
        add device request.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the driver's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiEjectDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine causes the specified device instance to be ejected (if
    the device is in a slot that supports soft-eject).

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance to be ejected.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the HAL bus extender that controls
        the bus where this device is located.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the HAL bus extender's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiUnlockDevice(
    IN  PUNICODE_STRING DeviceInstance,
    OUT PNTSTATUS ReturnedStatus
   )

/*++

Routine Description:

    This routine causes the specified device instance to be unlocked
    for removal (if the device is in a slot that supports slot locking).

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance to be unlocked.

    ReturnedStatus - If the function is successful, this receives the
        NT status code returned by the HAL bus extender that controls
        the bus where this device is located.

Return Value:

    NT status code indicating success or failure of this routine.  If
    STATUS_SUCCESS, then ReturnedStatus must be checked to determine
    the HAL bus extender's response to the request.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

//
// BUGBUG (lonnym): un-comment the 2nd parameter to the following
// routine once KenR defines the SLOT_CAPABILITIES structure.
//
NTSTATUS
PiQueryDeviceCapabilities(
    IN  PUNICODE_STRING DeviceInstance
// ,OUT PSLOT_CAPABILITIES Capabilities
    )

/*++

Routine Description:

    This routine returns the capabilities of a particular device (e.g.,
    is the device soft-ejectable?).

    (Note that while these capabilities are really an attribute of the
    particular bus slot on which the device resides, the user-mode PnP
    manager does not track bus slots--only devices.  Therefore, from
    user-mode, these attributes are associated with a device instance,
    and the kernel-mode PnP manager makes the association between device
    instances and actual bus slots.)

Arguments:

    DeviceInstance - Supplies the path in the registry (relative to
        HKLM\System\Enum) to the device instance whose capabilities
        are to be determined.

    Capabilities - Pointer to a buffer that receives the capabilities of
        the slot in which this device instance is located.

Return Value:

    NT status code indicating success or failure of this routine.

--*/

{
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
PiGetDevicePathInformation(
    IN  PUNICODE_STRING DevicePath,
    OUT PWCHAR ServiceName,
    IN  ULONG  BufferLength,
    OUT PULONG ReturnLength,
    OUT PULONG ServiceNameLength,
    OUT PULONG DeviceInstanceOffset,
    OUT PULONG DeviceInstanceLength,
    OUT PULONG ServiceInstanceOrdinal OPTIONAL
    )

/*++

Routine Description:

    This routine takes as input an NT device path, and returns the
    corresponding service name and (if possible) device instance it
    is associated with.  It also optionally returns the device instance's
    service ordinal as listed under the service entry's volatile Enum subkey.

Arguments:

    DevicePath - Supplies the NT device path

    ServiceName - Pointer to a character buffer that receives both
        the ServiceName _and_ DeviceInstance strings.  The
        ServiceName will be stored at the beginning of the buffer,
        and the DeviceInstance string (if applicable) will be stored
        at offset DeviceInstanceOffset in the buffer.  Both strings will
        be NULL-terminated.

    BufferLength - Supplies the length, in bytes, of the buffer pointed
        to by ServiceName.

    ReturnLength - Receives the size, in bytes, required to store
        both strings in the ServiceName buffer.  If the buffer isn't
        large enough, then no data will be stored in it, and this value
        will indicate the size necessary to store the data.

    ServiceNameLength - Receives the length, in bytes, of the ServiceName
        string stored in the ServiceName buffer (not including terminating
        NULL).

    DeviceInstanceOffset - If applicable, this value will receive the offset
        from the beginning of the ServiceName buffer (in characters) where the
        DeviceInstance string is located.  If no device instance is associated
        with this device path (e.g., the device path was created by a legacy
        driver), then this value will be set to zero.

    DeviceInstanceLength - Receives the length, in bytes, of the DeviceInstance
        string stored in the ServiceName buffer (not including terminating
        NULL).  If there is no associated device instance, then this value will
        be set to zero.

    ServiceInstanceOrdinal - If specified, receives the ordinal of the
        device instance within the service's volatile Enum list. If there is no
        associated device instance (i.e., DeviceInstanceOffset and
        DeviceInstanceLength are zero), then this value is set to PLUGPLAY_NO_INSTANCE.

Return Value:

    NT status code indicating success or failure of this routine.

--*/

{
    NTSTATUS Status;
    PFILE_OBJECT FileObject;
    PUNICODE_STRING DriverServiceName;
    HANDLE ServiceEnumHandle;
    PI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
    ULONG ReturnedServiceOrdinal, DevInstStringLength;

    //
    // Get a pointer to a file object for the specified device so that we can
    // retrieve the controlling service name from its driver object.
    //
    Status = PiGetDeviceObjectFilePointer(DevicePath,
                                          &FileObject
                                         );
    if(!NT_SUCCESS(Status)) {
        return Status;
    }

    DriverServiceName = &(FileObject->DeviceObject->DriverObject->DriverExtension->ServiceKeyName);

    //
    // The driver object may not have an associated service name, so only search
    // for a service instance if it does.
    //
    if(DriverServiceName->Length) {
        //
        // Now search through each device instance listed under the service
        // entry's volatile Enum subkey, looking for a match in one of the
        // NtDevicePaths REG_MULTI_SZ value entries.
        //
        DevPathToSvcInstContext.ReturnStatus = STATUS_SUCCESS;
        DevPathToSvcInstContext.DevicePath = DevicePath;
        RtlInitUnicodeString(&(DevPathToSvcInstContext.DeviceInstanceMatch), NULL);

        //
        // We need to acquire the PnP device registry resource for shared (read) access.
        //
        KeEnterCriticalRegion();
        ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);

        Status = IopApplyFunctionToServiceInstances(NULL,
                                                    DriverServiceName,
                                                    KEY_READ,
                                                    TRUE,
                                                    PiDevicePathToServiceInstance,
                                                    &DevPathToSvcInstContext,
                                                    &ReturnedServiceOrdinal
                                                   );

        ExReleaseResource(&PpRegistryDeviceResource);
        KeLeaveCriticalRegion();

        if(NT_SUCCESS(Status) &&
           NT_SUCCESS(Status = DevPathToSvcInstContext.ReturnStatus)) {

            DevInstStringLength = DevPathToSvcInstContext.DeviceInstanceMatch.Length;
        } else {
            goto PrepareForReturn1;
        }

    } else {
        DevInstStringLength = 0;
    }

    //
    // Now we know the size of the string buffer required, so check to make sure
    // the buffer we were given is large enough, and if so, fill it with the
    // string(s).
    //
    *ReturnLength = DriverServiceName->Length + DevInstStringLength
                         + ((DevInstStringLength) ? 2 : 1) * sizeof(WCHAR);

    if(BufferLength < *ReturnLength) {
        Status = STATUS_BUFFER_TOO_SMALL;
        goto PrepareForReturn2;
    }

    *ServiceNameLength = DriverServiceName->Length;

    if(*ServiceNameLength) {
        RtlMoveMemory(ServiceName,
                      DriverServiceName->Buffer,
                      *ServiceNameLength
                     );
    }

    ServiceName[CB_TO_CWC(*ServiceNameLength)] = UNICODE_NULL;

    if(*DeviceInstanceLength = DevInstStringLength) {
        RtlMoveMemory(&(ServiceName[*DeviceInstanceOffset = CB_TO_CWC(*ServiceNameLength) + 1]),
                      DevPathToSvcInstContext.DeviceInstanceMatch.Buffer,
                      *DeviceInstanceLength
                     );
        ServiceName[*DeviceInstanceOffset + CB_TO_CWC(*DeviceInstanceLength)] = UNICODE_NULL;
    } else {
        *DeviceInstanceOffset = 0;
    }

    if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
        *ServiceInstanceOrdinal = DevInstStringLength ? ReturnedServiceOrdinal
                                                      : PLUGPLAY_NO_INSTANCE;
    }

PrepareForReturn2:

    if(DevInstStringLength) {
        ExFreePool(DevPathToSvcInstContext.DeviceInstanceMatch.Buffer);
    }

PrepareForReturn1:

    ObDereferenceObject(FileObject);
    return Status;
}

BOOLEAN
PiDevicePathToServiceInstance(
    IN     HANDLE DeviceInstanceHandle,
    IN     PUNICODE_STRING DeviceInstancePath,
    IN OUT PVOID Context
    )

/*++

Routine Description:

    This routine is a callback function for IopApplyFunctionToServiceInstances.
    It is called for each device instance key referenced by a service instance
    value under the specified service's volatile Enum subkey. Its purpose is to
    determine whether this device instance corresponds to a specified NT device
    path (as registered in the device instance's NtDevicePaths REG_MULTI_SZ list).

    NOTE: The PnP device-specific registry resource must be acquired for shared
    (read) access before invoking this routine.

Arguments:

    DeviceInstanceHandle - Supplies a handle to the current device instance key.
        The access to this key is that specified in the call to
        IopApplyFunctionToServiceInstances.

    DeviceInstancePath - Supplies the registry path (relative to HKLM\System\Enum)
        to this device instance.

    Context - Supplies a pointer to a PI_DEVPATH_TO_SVCINST_CONTEXT structure with
        the following fields:

        NTSTATUS ReturnStatus - Fill this in with the NT error status code if an
             error occurs. This is assumed to be initialized to STATUS_SUCCESS
             when this routine is called.

        PUNICODE_STRING DevicePath - Supplies the NT device path that we're looking
            for.

        UNICODE_STRING DeviceInstanceMatch - If the current device instance corresponds
            to the specified NT device path, then fill this unicode string in with
            the device instance path.  The caller is responsible for freeing the
            (PagedPool) memory allocated for the unicode string buffer.

Return Value:

    TRUE to continue the enumeration.
    FALSE to abort it. If the current device instance corresponds to the NT device
        path being searched for, this function should return FALSE to terminate the
        search.

--*/

{
    PPI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
    NTSTATUS Status;
    PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
    PUNICODE_STRING DevicePathList;
    ULONG DevicePathCount, i;

    DevPathToSvcInstContext = (PPI_DEVPATH_TO_SVCINST_CONTEXT)Context;

    //
    // Retrieve the NtDevicePath REG_MULTI_SZ list from the device instance key.
    //
    Status = IopGetRegistryValue(DeviceInstanceHandle,
                                 REGSTR_VALUE_NTDEVICEPATHS,
                                 &KeyValueInformation
                                );
    if(!NT_SUCCESS(Status)) {
        //
        // Ignore this device instance and continue search
        //
        return TRUE;
    }

    Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
                                           &DevicePathList,
                                           &DevicePathCount
                                          );
    ExFreePool(KeyValueInformation);

    if(!NT_SUCCESS(Status)) {
        //
        // An error here is most likely STATUS_INSUFFICIENT_RESOURCES, which is
        // severe enough to abort the search.
        //
        DevPathToSvcInstContext->ReturnStatus = Status;
        return FALSE;
    }

    //
    // Now, search the device path list, looking for a match.
    //
    for(i = 0; i < DevicePathCount; i++) {

        if(RtlEqualUnicodeString(&(DevicePathList[i]),
                                 DevPathToSvcInstContext->DevicePath,
                                 TRUE)) {
            //
            // We found a match, so store a copy of the device instance path string
            // in the DeviceInstanceMatch field of the context structure.
            //
            if(!IopConcatenateUnicodeStrings(&(DevPathToSvcInstContext->DeviceInstanceMatch),
                                             DeviceInstancePath,
                                             NULL
                                            )) {

                DevPathToSvcInstContext->ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
            }
            break;
        }
    }

    IopFreeUnicodeStringList(DevicePathList, DevicePathCount);

    return (i == DevicePathCount) ? TRUE : FALSE;
}
#endif // _PNP_POWER_




NTSTATUS
PiDetectResourceConflict(
    IN PCM_RESOURCE_LIST  ResourceList,
    IN ULONG              ResourceListSize
    )

/*++

Routine Description:

    This routine is invoked to test whether the specified resource
    list conflicts with any already assigned resources.

Arguments:

    ResourceList - Specifies a resource list buffer.

    ResourceListSize - Specifies the size of the resource list buffer.

Return Value:

    The function value is an NTSTATUS value; STATUS_SUCCESS indicates
    that the resources do not conflict, STATUS_INSUFFICIENT_RESOURCES
    indicates that the resource conflict with already assigned
    resources (or some other NTSTATUS value may indicate a different
    internal error).

--*/

{
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE handle;
    PWSTR buffer;
    NTSTATUS status;
    UNICODE_STRING DriverName;
    ULONG i;
    BOOLEAN bConflictDetected = FALSE, bTemp;
    CM_RESOURCE_LIST EmptyResourceList;


    if (driverObject == NULL) {
        //
        // Driver object has not been created yet, do that now.
        //
        RtlInitUnicodeString(&DriverName, L"\\Device\\PlugPlay");

        //
        // Begin by creating the permanent driver object.
        //
        InitializeObjectAttributes(&objectAttributes,
                                   &DriverName,
                                   OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
                                   (HANDLE)NULL,
                                   (PSECURITY_DESCRIPTOR)NULL);

        //
        // Specify "KernelMode" here since it refers to the source of
        // the objectAttributes buffer, not the previous operating system
        // mode.
        //
        status = ObCreateObject(KernelMode,
                                IoDriverObjectType,
                                &objectAttributes,
                                KernelMode,
                                (PVOID)NULL,
                                (ULONG)(sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION)),
                                0,
                                0,
                                (PVOID)&driverObject);

        if (!NT_SUCCESS(status)) {
            return status;
        }

        //
        // Initialize the driver object.
        //
        RtlZeroMemory(driverObject,
                      sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION));
        driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
        driverObject->DriverExtension->DriverObject = driverObject;
        for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
            driverObject->MajorFunction[i] = NULL;        // OK???
        }
        driverObject->Type = IO_TYPE_DRIVER;
        driverObject->Size = sizeof(DRIVER_OBJECT);
        driverObject->DriverInit = NULL;

        //
        // Insert the driver object into the object table.
        //
        status = ObInsertObject(driverObject,
                                NULL,
                                FILE_READ_DATA,
                                0,
                                (PVOID *)NULL,
                                &handle);

        if (!NT_SUCCESS(status)) {
            ObMakeTemporaryObject(driverObject);    //?
            ObDereferenceObject(driverObject);      //?
            return status;
        }

        //
        // Save the name of the driver so that it can be easily located by functions
        // such as error logging.
        //
        buffer = ExAllocatePool(PagedPool, DriverName.MaximumLength + 2);

        if (buffer) {
            driverObject->DriverName.Buffer = buffer;
            driverObject->DriverName.MaximumLength = DriverName.MaximumLength;
            driverObject->DriverName.Length = DriverName.Length;

            RtlCopyMemory(driverObject->DriverName.Buffer,
                          DriverName.Buffer,
                          DriverName.MaximumLength);
            buffer[DriverName.Length >> 1] = (WCHAR) '\0';
        }
    }

    //
    // Attempt to acquire the resource, if successful, we know the
    // resource is avaiable, overwise assume it conflicts with another
    // devices resource's.
    //
    status = IoReportResourceUsage(NULL,
                                   driverObject,
                                   ResourceList,
                                   ResourceListSize,
                                   NULL,
                                   NULL,
                                   0,
                                   TRUE,
                                   &bConflictDetected);

    if (NT_SUCCESS(status)) {
        //
        // Clear any resources that might have been assigned to my fake device.
        //
        RtlZeroMemory(&EmptyResourceList, sizeof(CM_RESOURCE_LIST));

        IoReportResourceUsage(NULL,
                              driverObject,
                              &EmptyResourceList,
                              sizeof(CM_RESOURCE_LIST),
                              NULL,
                              NULL,
                              0,
                              TRUE,
                              &bTemp);
    }


    if (NT_SUCCESS(status) && bConflictDetected) {
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return status;
}

